#pragma once
#include "util.hpp"
#include "onlinemanager.hpp"
#include "db.hpp"
#include "room.hpp"
#include "log.hpp"
#include <list>
#include <thread>
#include <mutex>
#include <condition_variable>


// 编写三个用于管理对应的段位的等待队列，其中这个对象需要被线程来管理
template<class T>
class match_queue
{
public:
	//  获取当前队列内的玩家数
	int size() 
	{ 
		unique_lock<mutex> lock(_mutex);
		return _q.size();
	}

	bool empty()
	{
		unique_lock<mutex> lock(_mutex);
		return _q.empty();
	}


	// 让当前线程进行等待
	void wait()
	{
		unique_lock<mutex> lock(_mutex);
		_cond.wait(lock);
	}

	bool push(T val)
	{
		unique_lock<mutex> lock(_mutex);
		_q.push_back(val);
		_cond.notify_all();
		return true;
	}

	bool pop(T& val)
	{
		unique_lock<mutex> lock(_mutex); 
		if(_q.empty()) return false;
		val = _q.front();
		_q.pop_front();
		return true;
	}

	void remove(T &data) {
		unique_lock<mutex> lock(_mutex);
		_q.remove(data);
		}
private:
	mutex _mutex;
	// 使用条件变量阻塞住消费者线程，因为当玩家数<2时是无法匹配的
	condition_variable _cond;
	// 不能直接使用队列，因为队列只能出队头队尾
	list<T> _q;
};


class matcher
{
public:
	matcher(online_manager* olm,user_table* ust,room_manager* rm)
		:_olm(olm),_usrt(ust),_rm(rm),
		_no_thread(thread(&matcher::no_thread_handler,this)),
		_ex_thread(thread(&matcher::ex_thread_handler,this)),
		_ma_thread(thread(&matcher::ma_thread_handler,this))
	{
		DLOG("游戏匹配模块初始化完成！");
	}

	bool add(uint64_t user_id)
	{
		Json::Value user;
		bool ret = _usrt->select_by_id(user_id,user);
		if(ret == false) 
		{
			DLOG("获取玩家%d的信息失败！",user_id);
			return false;
		}
		// 将取出的用户信息结果中的分数进行选择
		// 1500分以下都是普通，1500到2000是高手，2000到2500就是大师

		uint64_t score = user["score"].asUInt64();
		DLOG("玩家%d的分数是%d ",user_id,score);

		if(score<=1500)		_normal.push(user_id);
		else if(score>1500 && score <=2000 )	_expert.push(user_id);
		else if(score>2000) _master.push(user_id);

		DLOG("当前大厅队列的人数是%d ",_normal.size());

		return true;

	}


	bool del(uint64_t user_id)
	{
		Json::Value user;
		bool ret = _usrt->select_by_id(user_id,user);
		
		if(ret == false) 
		{
			DLOG("获取玩家%d的信息失败！",user_id);
			return false;
		}
		uint64_t score = user["score"].asUInt64();
		if(score<=1500)		_normal.remove(user_id);
		else if(score>1500 && score <=2000 )	_expert.remove(user_id);
		else if(score>2000) _master.remove(user_id);
		return true;
	}
private:
	
	void handler_match(match_queue<uint64_t>& queue)
	{
		// 1.等待当前队列内的玩家数量达到要求值
		while(1)
		{
			while(queue.size() < 2)
			{
				DLOG("当前队列内人数:%d 正在等待",queue.size());
				queue.wait();
			}

			uint64_t user1,user2;
			// 2.此时已经凑齐了两名玩家,将其二人先出队列
			DLOG("出队列玩家1");
			bool ret = queue.pop(user1);
			if(ret == false)
			{
				// 如果获取第一个用户失败了，那么就返回等待状态
				continue;
			}

			if(!queue.pop(user2))
			{
				DLOG("出队列玩家2");

				// 如果获取第二个用户失败了，那么就先把第一个用户放回等待队列再进入等待状态
				queue.push(user1);
				continue;
			}

			// 3.已获取两个用户，此时获取它俩的链接,如果有⼈掉线，则要吧另⼀个⼈重新添加⼊队列
			wsvr_t::connection_ptr conn1 = _olm->get_conn_from_game_hall(user1);
			if(conn1 == nullptr)
			{
				this->add(user1);
				continue;
			}

			wsvr_t::connection_ptr conn2 = _olm->get_conn_from_game_hall(user2);
			if(conn1 == nullptr)
			{
				this->add(user2);
				continue;
			}


			DLOG("两名玩家都没有临时掉线，尝试创建房间！");

			// 4.为两个玩家创建房间，并将他们加入到房间中
			room_ptr rp = _rm->room_create(user1,user2);
			if(rp == nullptr)
			{
				// 如果房间创建失败，就把两个玩家重新加入队列
				this->add(user1);
				this->add(user2);
				continue;
			}

			DLOG("匹配成功");

			// 5.对两个玩家返回一次响应
			Json::Value resp;
			resp["optype"] = "match_success";
			resp["result"] = true;
			string body;

			Json_util::serialize(resp,body);

			conn1->send(body);
			conn2->send(body);


		}
	}


	void no_thread_handler() { handler_match(_normal);}

	void ex_thread_handler() { handler_match(_expert);}

	void ma_thread_handler() { handler_match(_master);}


private:
	// 3个执行对应函数的线程，以及三个匹配队列
	match_queue<uint64_t> _normal;
	match_queue<uint64_t> _expert;
	match_queue<uint64_t> _master;
	thread _no_thread;
	thread _ex_thread;
	thread _ma_thread;
	online_manager *_olm;
	user_table *_usrt;
	room_manager* _rm;
};